#include "buffer_alloc.hpp"
#include "buffer.hpp"

namespace mina
{
	Buffer::Buffer(int32_t size)
	{
		_start  = ((char*)this) + sizeof(*this);
		_end    = ((char*)this) + size;
		_consumer = _producer = _start;

		_next = NULL;
	}

	int32_t Buffer::consume(int32_t nbytes)
	{
		nbytes     = std::min(bytes_consumable(), self_or_max(nbytes));

		_consumer += nbytes;

		return nbytes;
	}

	/// advance _producer
	int32_t Buffer::produce(int32_t nbytes)
	{
		nbytes      = std::min(bytes_producible(), self_or_max(nbytes));

		_producer  += nbytes;

		return nbytes;
	}

	void Buffer::copy_in(char ch)
	{
		assert(bytes_producible() > 0);

		*_producer  = ch;
	}

	int32_t Buffer::copy_in(const char * data, int32_t nbytes)
	{
		nbytes = std::min(bytes_producible(), self_or_max(nbytes));

		::memmove(producer(), data, nbytes);

		return nbytes;
	}

	int32_t Buffer::copy_in(const Buffer & buffer, int32_t nbytes)
	{
		nbytes = std::min(buffer.bytes_consumable(), self_or_max(nbytes));

		return copy_in(const_cast<Buffer&>(buffer).consumer(), nbytes);
	}

	int32_t Buffer::copy_in(const std::string & str, int32_t nbytes)
	{
		nbytes = std::min((int32_t)str.size(), self_or_max(nbytes));

		return copy_in(str.c_str(), nbytes);
	}

	void Buffer::copy_out(char & ch)
	{
		assert(bytes_consumable() > 0);

		ch = *_consumer;
	}

	int32_t Buffer::copy_out(char * data, int32_t nbytes)
	{
		nbytes  = std::min(bytes_consumable(), self_or_max(nbytes));
		
		::memmove(consumer(), data, nbytes);

		return nbytes;
	}

	int32_t Buffer::copy_out(std::string & str, int32_t nbytes)
	{
		nbytes = std::min(bytes_consumable(), self_or_max(nbytes));

		str.append(consumer(), nbytes);

		return nbytes;
	}

	int32_t Buffer::copy_out(Buffer & buffer, int32_t nbytes)
	{
		nbytes  = std::min(buffer.bytes_producible(), self_or_max(nbytes));

		return copy_out(buffer.producer(), nbytes);
	}

	int32_t Buffer::read(int fd, int32_t nbytes)
	{
		nbytes  = std::min(bytes_producible(), self_or_max(nbytes));

		int32_t nread = ::read(fd, producer(), nbytes);

		if (nread > 0)
		{
			produce(nread);
		}

		return (int32_t)(nread >= 0? nread: (errno > 0? -errno: nread));
	}
	
	void Buffer::append(char ch)
	{
		copy_in(ch);
		produce(1);
	}

	void Buffer::move_to(char & ch)
	{
		copy_out(ch);
		consume(1);
	}
	
	std::string Buffer::to_string() const
	{
		std::ostringstream oss;
		/*
		oss << (uint64_t) _start << " "
			<< (uint64_t) _end   << " "
			<< (uint64_t) _producer << " "
			<< (uint64_t) _consumer << " "
			<< (uint64_t) _next << " "
			<< "consumable: " << bytes_consumable() << " ";
		
		std::string str;
		if (bytes_consumable() > 64)
		{
			str.assign(_consumer, 64);
		}
		else
		{
			str.assign(_consumer, bytes_consumable());
		}

		oss << str;
		*/
		return oss.str();
	}

}

